/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.rie06.openvoip.client;

import com.rie06.openvoip.User;
import com.rie06.openvoip.client.IHM.communication.WindowDialog;
import com.rie06.openvoip.client.IHM.home.InterfaceClient;
import com.rie06.openvoip.server.Server;
import com.rie06.openvoip.server.ServerMessage;
import com.rie06.openvoip.server.ServerUDP;
import java.io.*;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;

/**
 * Cette classe permet de créer un client, ce client pourra créer une
 * conversation avec un autre client, rajouter du monde à cette même
 * conversation .....
 */
public class Client {

    private static final Logger logger = Logger.getLogger(Client.class);
    private Server server;
    private ServerUDP serverUDP;
    private InterfaceClient ihm;
    private boolean connectionActive;
    private Map contactList;
    private Map contactListTemp;
    private Map conversationList;
    private boolean debug;
    private UUID currentConversation;

    public Client(Server server, ServerUDP serverUDP, InterfaceClient ihm) {
        Construct(server, serverUDP, ihm, false);
    }

    public Client(Server server, ServerUDP serverUDP, InterfaceClient ihm, boolean debug) {
        Construct(server, serverUDP, ihm, debug);
    }

    private void Construct(Server server, ServerUDP serverUDP, InterfaceClient ihm, boolean debug) {
        this.server = server;
        this.serverUDP = serverUDP;
        this.ihm = ihm;
        this.connectionActive = false;
        this.debug = debug;
        this.conversationList = new HashMap();
        this.currentConversation = null;

        server.setClient(this);
        serverUDP.setClient(this);
        ihm.setClient(this);

        contactList = new HashMap();
        contactListTemp = new HashMap();

        //get/set Contact list

        String configFile = System.getProperty("user.home") + "/.config/OpenVoip/Contacts.list";
        contactList = initializeContactList(configFile);
        configFile = System.getProperty("user.home") + "/.config/OpenVoip/Contacts.list.temp";
        contactListTemp = initializeContactList(configFile);

        //start thread to manage Contact List
        Client_UpdateContact client_UpdateContact = new Client_UpdateContact(this);
        Client_UpdateContactTemp client_UpdateContactTemp = new Client_UpdateContactTemp(this);

    }

    /**
     * initialise la liste de contact du client
     *
     */
    private Map initializeContactList(String configFile) {
        Map map = new HashMap();
        Map mapFinal = new HashMap();
        if (!debug) {
            boolean exists = (new File(configFile)).exists();
            if (!exists) {
                FileOutputStream fout = null;
                try {
                    //generate UUID and serialize config
                    map = new HashMap();
                    fout = new FileOutputStream(configFile);
                    ObjectOutputStream oos;
                    oos = new ObjectOutputStream(fout);
                    oos.writeObject(map);
                    oos.close();
                    mapFinal.putAll(map);
                } catch (FileNotFoundException ex) {
                    java.util.logging.Logger.getLogger(Client.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
                } catch (IOException ex) {
                    java.util.logging.Logger.getLogger(Client.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
                } finally {
                    try {
                        fout.close();
                    } catch (IOException ex) {
                        java.util.logging.Logger.getLogger(Client.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
                    }
                }
            } else {
                ObjectInputStream ois = null;
                //unserialize config file
                try {
                    FileInputStream fin;
                    fin = new FileInputStream(configFile);
                    ois = new ObjectInputStream(fin);
                    map = (Map) ois.readObject();
                    ois.close();
                    mapFinal.putAll(map);
                    //                   logger.log(Level.INFO, server.getServerConfig().getUserName() + " - Load file: " + configFile);
                } catch (IOException ex) {
                    java.util.logging.Logger.getLogger(Client.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
                } catch (ClassNotFoundException ex) {
                    java.util.logging.Logger.getLogger(Client.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);

                } finally {
                    try {
                        ois.close();
                    } catch (IOException ex) {
                        java.util.logging.Logger.getLogger(Client.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
                    }
                }
            }
        }
        return mapFinal;
    }

    /**
     * sauvegarde la liste de contacts
     *
     */
    public void saveContactList(String file, Map map) {
        if (!debug) {
            boolean exists = (new File(file)).exists();
            if (exists) {
                FileOutputStream fout = null;
                try {
                    fout = new FileOutputStream(file);
                    ObjectOutputStream oos;
                    oos = new ObjectOutputStream(fout);
                    oos.writeObject(map);
                    oos.close();
                    //                   logger.log(Level.INFO, "Save in file: " + file);
                } catch (FileNotFoundException ex) {
                    java.util.logging.Logger.getLogger(Client.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
                } catch (IOException ex) {
                    java.util.logging.Logger.getLogger(Client.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
                } finally {
                    try {
                        fout.close();
                    } catch (IOException ex) {
                        java.util.logging.Logger.getLogger(Client.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
                    }
                }
            } else {
                logger.log(Level.ERROR, "Unabled to save Object in file: " + file);
            }
        }
    }

    /**
     * Permet d'ajouter un contact
     *
     */
    public void addContact(InetAddress ipAddress, Integer portserver) {
        //       logger.log(Level.DEBUG, "Add Contact to TempList: " + ipAddress.toString() + ":" + portserver);
        Contact contact = new Contact(ipAddress, portserver);
        contactListTemp.put(contact.getUuid(), contact);
        //       logger.log(Level.DEBUG, "List contactListTemp: " + contactListTemp.size());
        //save contact list temp
        String configFile = System.getProperty("user.home") + "/.config/OpenVoip/Contacts.list.temp";
        saveContactList(configFile, contactListTemp);
    }

    /**
     * Permet d'ajouter un contact à sa liste
     *
     */
    public void addContactToList(UUID uuid, String nom, InetAddress ipAddress, Integer portserver) {
//        logger.log(Level.DEBUG, "Add Contact: " + ipAddress.toString() + ":" + portserver);
        Contact contact = new Contact(uuid, nom, ipAddress, portserver);
        if (!contactList.containsKey(contact.getUuid())) {
            contactList.put(contact.getUuid(), contact);
            //save contact list
            String configFile = System.getProperty("user.home") + "/.config/OpenVoip/Contacts.list";
            saveContactList(configFile, contactList);
        }
    }

    /**
     * Permet de se connecter au serveur
     *
     */
    public User connectTo(InetAddress ipserver, Integer portserver, String name) {
        User test = null;
        if (!connectionActive) {
            try {
                Socket socket = new Socket(ipserver, portserver);

                ObjectOutput out = new ObjectOutputStream(socket.getOutputStream());
                out.flush();
                ObjectInput in = new ObjectInputStream(socket.getInputStream());

                //send UUID
                ServerMessage smOut = new ServerMessage("uuid", server.getServerConfig().getUserName(), server.getServerConfig().getId());
                out.writeObject(smOut);
                out.flush();

                //get UUID
                ServerMessage smIn;
                smIn = (ServerMessage) in.readObject();

                if (!smIn.getType().equals("uuid")) {
                    throw new IllegalArgumentException("Need to have an UUID");
                }

                User user;
                if (name.equals("")) {
                    user = new User(smIn.getFrom(), (String) smIn.getMessage());
                } else {
                    user = new User(smIn.getFrom(), name);
                }

                test = user;

                //Add contact to server
                server.addFromClient(socket, in, out, user);
            } catch (IOException ex) {
                logger.log(Level.FATAL, ex);
            } catch (ClassNotFoundException ex) {
                java.util.logging.Logger.getLogger(Client.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
            }

        }
        return test;
    }

    /**
     * Créer une conversation avec un contact
     *
     */
    public Conversation createConversation(Contact contact) {
        Conversation conversation = new Conversation(this, UUID.randomUUID());
        conversation.addContact(contact);
        conversationList.put(conversation.getUuid(), conversation);
        //send to IHM
        ihm.getPanelRequest().updateListRequest();

        return conversation;
    }

    /**
     * Ajoute un contact à la conversation
     *
     */
    public void addConversation(UUID uuid, User user) {
        Contact contact = (Contact) contactList.get(user.getId());
        Conversation conversation = new Conversation(this, uuid, contact, contact.getUuid());
        conversationList.put(uuid, conversation);
        WindowDialog wd = new WindowDialog(ihm.getCommunicationController(), conversation, false);
        //send to IHM
        ihm.getPanelRequest().updateListRequest();
    }

    /**
     * Ferme une conversation
     *
     */
    public void closeConversation(UUID uuid) {
        Conversation c = (Conversation) conversationList.get(uuid);
        HashMap mp = (HashMap) c.getConversationPlayer();
        for (Iterator it = mp.entrySet().iterator(); it.hasNext();) {
            Map.Entry<UUID, ConversationPlayer> entry = (Map.Entry<UUID, ConversationPlayer>) it.next();

            UUID uuidmp = entry.getKey();
            ConversationPlayer player = entry.getValue();

            player.stopConversation();
        }
        c.getConversationRecorder().stopConversation();

        conversationList.remove(uuid);
        if(currentConversation != null && currentConversation.equals(uuid)){
            currentConversation=null;
        }
        ihm.getPanelRequest().updateListRequest();
    }

    /**
     * Envoie un message texte
     *
     */
    public int sendMessage(UUID to, String message) {
        ServerMessage m = new ServerMessage("message", message, server.getServerConfig().getId());
        //Get User in Server List
        if (server.getUsersList().containsKey(to)) {
            User u = (User) server.getUsersList().get(to);
            //send tcp message
            return u.getServerTCPThread().sendMessage(m);
        } else {
            return 404;
        }
    }

    void sendUDP(byte[] total, DatagramSocket socket, Contact contact) {
        try {
            InetAddress client = contact.getIpAddress();

            // Send payload
            DatagramPacket packet = new DatagramPacket(total, total.length, client, contact.getPort());
            socket.send(packet);

        } catch (IOException ex) {
            java.util.logging.Logger.getLogger(Client.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (NullPointerException ex) {
        }
    }

    public Server getServer() {
        return server;
    }

    public InterfaceClient getIhm() {
        return ihm;
    }

    public Map getContactList() {
        return contactList;
    }

    public Map getContactListTemp() {
        return contactListTemp;
    }

    public Map getConversationList() {
        return conversationList;
    }

    public UUID getCurrentConversation() {
        return currentConversation;
    }

    public void setCurrentConversation(UUID currentConversation) {
        this.currentConversation = currentConversation;
    }
}
